package com.gitee.wsl.struct.grammar.parser.tokenizer

import com.gitee.wsl.struct.grammar.parser.token.Token
import com.gitee.wsl.struct.grammar.parser.token.Token.Companion.noneMatched
import com.gitee.wsl.struct.grammar.parser.token.TokenMatch


/** Stateful producer of tokens that yields [com.gitee.wsl.struct.grammar.lexer.Token]s from some inputs sequence that it is based upon, one by one */
 interface TokenProducer {
     fun nextToken(): TokenMatch?
}

fun TokenProducer(input: CharSequence, tokens: List<Token>): TokenProducer=DefaultTokenProducer( tokens,input)

private class DefaultTokenProducer(private val tokens: List<Token>, private val input: CharSequence) : TokenProducer {
    private val inputLength = input.length
    private var tokenIndex = 0
    private var pos = 0
    private var row = 1
    private var col = 1

    private var errorState = false

    override fun nextToken(): TokenMatch? {
        if (pos > input.lastIndex || errorState) {
            return null
        }

       @Suppress("ReplaceManualRangeWithIndicesCalls")
        for (index in 0 until tokens.size) {
            val token = tokens[index]
            val matchLength = token.match(input, pos)
            if (matchLength == 0)
                continue

            val result = TokenMatch(token,  pos, matchLength)

            for (i in pos until pos + matchLength) {
                if (input[i] == '\n') {
                    row++
                    col = 1
                } else {
                    col++
                }
            }

            pos += matchLength

            return result
        }

        errorState = true
        return TokenMatch(noneMatched,  pos, inputLength - pos)
    }
}


class TokenMatchesSequence(
    private val tokenProducer: TokenProducer,
    private val matches: ArrayList<TokenMatch> = arrayListOf()
) : Sequence<TokenMatch> {

    private fun ensureReadPosition(position: Int): Boolean {
        while (position >= matches.size) {
            val next = tokenProducer.nextToken()
                ?: return false
            matches.add(next)
        }
        return true
    }

     operator fun get(position: Int): TokenMatch? {
        if (!ensureReadPosition(position)) {
            return null
        }
        return matches[position]
    }

     fun getNotIgnored(position: Int): TokenMatch? {
        if (!ensureReadPosition(position)) {
            return null
        }

        var pos = position
        while (true) {
            val value = if (pos < matches.size)
                matches[pos]
            else {
                val next = tokenProducer.nextToken()
                if (next == null)
                    return null
                else {
                    matches.add(next)
                    next
                }
            }
            if (!value.token.ignored)
                return value
            pos++
        }
    }

    override fun iterator(): Iterator<TokenMatch> =
        object : AbstractIterator<TokenMatch>() {
            var position = 0
            var noneMatchedAtThisPosition = false

            override fun computeNext() {
                if (noneMatchedAtThisPosition) {
                    done()
                }

                val nextMatch = get(position) ?: run { done(); return }
                setNext(nextMatch)
                if (nextMatch == noneMatched) {
                    noneMatchedAtThisPosition = true
                }
                ++position
            }
        }
}

